/* -*-  Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 2000 Massachusetts Institute of Technology.
 *
 * This software is being provided by the copyright holders under the GNU
 * General Public License, either version 2 or, at your discretion, any later
 * version. (See the file `LICENSE' in the source distribution.)
 *
 * This software is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The entire risk as
 * to the quality and performance of the program is with you. See the GNU
 * General Public License for more details.
 *
 * The name and trademarks of copyright holders may NOT be used in 
 * advertising or publicity pertaining to the software without specific, 
 * written prior permission. Title to copyright in this software and any 
 * associated documentation will at all times remain with copyright 
 * holders. See the file AUTHORS which should have accompanied this software 
 * for a list of all copyright holders. 
 * 
 * Particular files in this distribution may be derived from previously
 * copyrighted software. This copyright applies only to those changes made by
 * the copyright holders listed in the AUTHORS file.
 */
/*
 * An implementation of the Congestion Manager (CM) for Linux.
 * This file Implements congestion-controlled UDP sockets.
 */
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/inet.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <net/route.h>
#include <net/cm_proto.h>
#include <net/cm.h>
#include <net/cm_debug.h>

/* 
 * Link skb to the list of data pending transmission, and then calls
 * cm_request() if data has come in empty list.
 */
int udp_ccsend(struct sock *sk, struct sk_buff *skb) 
{
	struct sk_buff *tmp=SK_CM_FLOW(sk)->data;
	int retval=0;
	if (tmp==NULL){		//handle base case 
		SK_CM_FLOW(sk)->data=skb;
		/* 
		 * XXX commented lines below because need to figure out
		 * what to put in the changed structure here.
		   if ((sk->tp_cm4.peersk) == NULL) 
		   printk("DO CONNECT FIRST: udp_ccsend\n"); 
		   else 
		   printk("CM_PEERSK not null\n"); 
		*/
		retval=cm_request(SK_CM_FLOW(sk));
		return retval;
	} else {
		while (tmp->next != NULL)
			tmp=tmp->next;
		tmp->next=skb;
	}
	skb->next=NULL;
	return 0;
	
}

/* 
 * Callback function for cc-UDP.  sk is the data socket.
 */
void udp_ccbufsend(struct cm_flow *flow)
{
	
	struct sock *sk = SK_CM_PEERSK(flow->sk);
	struct sk_buff *skb=SK_CM_FLOW(sk)->data;
	struct rtable *rt=NULL;
	int count = 0;
	int done = 0;
	
	if (skb==NULL){
		CM_DPRINTF(CM_DEBUG_ERRS,
			   "cm_debug error: %s: Null sk_buff in data list\n",
			   __FUNCTION__);
		return;
	}
	count = skb->appdata;
	while (!done) {
		SK_CM_FLOW(sk)->data=skb->next;
		/* 
		 * We find rt just as in udp.c; only case to worry about is
		 * if connected.  Pass rt table as parameter.
		 * Otherwise, we need to take care of all cases as in udp.c 
		 */
		rt = (struct rtable*)dst_clone(sk->dst_cache);
		rt->u.dst.output(skb);
		//sk is data socket, need to call cm_notify on control socket
		cm_notify(flow,(int)(skb->appdata));
		skb = SK_CM_FLOW(sk)->data;
		if (skb  == NULL) 	
			done = 1;
		else {
			count = count + skb->appdata;
			if (count > flow->mflow->mtu) 
				done = 1;
		} 
	}
	flow->pending = CM_REQUEST_INACTIVE;
	if (SK_CM_FLOW(sk)->data != NULL) /* data socket here */
		cm_request(flow);         /* control socket here */
}
